package de.fru;

import java.io.IOException;
import java.io.InputStream;
import java.util.Formatter;

public class ByteStream {

	InputStream input;

	private int lineCount = 0;

	private int lineByte = -1;
	
	private int byteCount = 0;

	public ByteStream(InputStream input) {
		this.input = input;
	}

	int maxReads = -1;

	public ByteStream(ByteStream reader, int maxReads) {
		this.input = reader.input;
		this.maxReads = maxReads;
	}

	public int findNext() {
		if (maxReads == 0)
			throw new EofException();
		maxReads--;
		int out = -1;
		try {
			out = input.read();
		} catch (IOException ex) {
			// Do nothing
		}
		if (out == -1) {
			throw new EofException();
		}
		lineByte++;
		byteCount++;
		if (out == '\n') {
			lineCount++;
			lineByte = 0;
		}
		return out;
	}

	public int findInt() {
		return (findNext() << 24) + ((findNext() & 0xFF) << 16)
				+ ((findNext() & 0xFF) << 8) + (findNext() & 0xFF);
	}

	public int findShort() {
		return ((findNext() & 0xFF) << 8) + (findNext() & 0xFF);
	}

	public long findLong() {
		return ((long) findInt() << 32) + (findInt() & 0xFFFFFFFFL);
	}

	public static String format(Object data) {
		Formatter formatter = new Formatter();
		try {
			if (data instanceof int[]) {
				boolean first = true;
				for (int b : (int[]) data) {
					if (!first)
						formatter.format(",");
					first = false;
					formatter.format("%02x", b);
				}
			} else if (data instanceof Integer) {
				formatter.format("%02x", (Integer) data);
			} else if (data instanceof String) {
				return (String) data;
			}
			return formatter.out().toString();
		} finally {
			formatter.close();
		}
	}

	public int getByteCount() {
		return byteCount;
	}
	
	public class EofException extends RuntimeException {

		private static final long serialVersionUID = -4195877774157187734L;

	}

	public class BytecodeException extends RuntimeException {

		private String message = null;

		public BytecodeException(String message) {
			this.message = message;
		}

		public BytecodeException(String message, Object... objects) {
			Object[] out = new Object[objects.length];
			for (int i = 0; i < out.length; i++) {
				out[i] = format(objects[i]);
			}
			this.message = String.format(message, out);
		}

		public BytecodeException() {
		}

		private static final long serialVersionUID = -5364296252420323542L;

		@Override
		public String toString() {
			String out = "BytecodeException @" + lineCount + "," + lineByte;
			if (message != null) {
				out += ": " + message;
			}
			return out;

		}
	}

	public static enum Type {
		String, Class, NameAndType, MethodRef, FieldRef, InterfaceMethodRef, Integer, Float, Long, Double, Utf8;
	}

	public static class Constant {

		String value = null;

		int ref1 = -1;

		int ref2 = -1;

		private Type type;

		public Constant(String value) {
			this.value = value;
		}

		public Constant(int ref1) {
			this.ref1 = ref1;
		}

		public Constant(int ref1, int ref2) {
			this.ref1 = ref1;
			this.ref2 = ref2;
		}

		public Type getType() {
			return type;
		}

		public void setType(Type type) {
			this.type = type;
		}

		public String toString() {
			String out = "";
			if (type != null)
				out += type + ": ";
			if (value != null) {
				out += value;
			} else if (ref2 != -1) {
				out += ref1 + " - " + ref2;
			} else {
				out += ref1;
			}
			return out;
		}
	}
}
